/* Copyright 2008 Bas van den Berg
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <string>

#include "yaffut.h"
#include "myassert.h"
#include "TestSession.h"
#include "TestRoom.h"

#include "MudDoor.h"
#include "Area.h"
#include "Room.h"
#include "MudExit.h"
#include "MudKey.h"
#include "MudPlayer.h"
#include "color.h"
#include "Global.h"
#include "TaskQueue.h"

using namespace std;
using namespace Chronos;

class MudDoorTest { 
public:
	MudDoorTest()
		: taskQueue(0)
		, session("player")
		, player(session.getPlayer())
		, area(0, "")
		, room1(new TestRoom("room1"))
		, room2(new TestRoom("room2"))
		, door(new MudDoor("door", "descr.", new RoomPortal(area, 2, "", "", ""), 4))
	{
        Global<TaskQueue>::set(&taskQueue);
		area.addRoom(1, *room1);
		area.addRoom(2, *room2);
		room1->addObject(door);
		room1->addPlayer(player, "");
		session.clear();
	}
	~MudDoorTest() {
		Room* room = player.getRoom();
		room->removePlayer(player, "");
	}

	TaskQueue taskQueue;
	TestSession session;
	MudPlayer& player;
	Area area;
	TestRoom* room1;
	TestRoom* room2;
	MudDoor* door;
};


TEST(MudDoorTest, testOpenClose) {
	door->open(player);
	session.SESSION_CHECKWRITTEN("OK\n");
	room1->ROOM_CHECKWRITTEN("player opens the door.\n");
    ASSERT_STR_EQUAL("descr. It's open.", door->getDescription());

	door->open(player);
	session.SESSION_CHECKWRITTEN("The door is already open.\n");

	door->close(player);
	session.SESSION_CHECKWRITTEN("OK\n");
	room1->ROOM_CHECKWRITTEN("player closes the door.\n");
    ASSERT_STR_EQUAL("descr. It's closed.", door->getDescription());

	door->close(player);
	session.SESSION_CHECKWRITTEN("The door is already closed.\n");
}


TEST(MudDoorTest, testPass) {
	door->pass(player);
	session.SESSION_CHECKWRITTEN("The door is closed.\n");

	door->open(player);
	session.SESSION_CHECKWRITTEN("OK\n");

	ASSERT_EQUAL(room1, player.getRoom());
	door->pass(player);
	session.SESSION_CHECKWRITTEN("room2\n");
	ASSERT_EQUAL(room2, player.getRoom());
}


TEST(MudDoorTest, testSelfClosingAfterDelay) {
    door->open(player);
    room1->clear();

    taskQueue.runNextTime();
    taskQueue.runNextTime();
    taskQueue.runNextTime();
	room1->ROOM_CHECKWRITTEN("");
    taskQueue.runNextTime();
	room1->ROOM_CHECKWRITTEN("The door closes.\n");
}


TEST(MudDoorTest, testSelfClosingAfterDelayWithInteruption) {
    door->open(player);
    room1->clear();

    taskQueue.runNextTime();
    door->close(player);

    taskQueue.runNextTime();
    door->open(player);
    room1->clear();

    taskQueue.runNextTime();
	room1->ROOM_CHECKWRITTEN("");

    taskQueue.runNextTime();
	room1->ROOM_CHECKWRITTEN("The door closes.\n");
}


class DoorTestPortal : public Portal {
public:
    DoorTestPortal() : passedPlayer(0) {}
    virtual void pass(MudPlayer& player) {
        passedPlayer = &player;
    }
    MudPlayer* passedPlayer;
};


class MudLockedDoorTest { 
public:
	MudLockedDoorTest()
		: taskQueue(0)
		, session("player")
		, player(session.getPlayer())
		, room1("room1")
        , portal(new DoorTestPortal())
		, door(new MudLockedDoor("door", "descr.", portal, 4))
        , key(0, "key", "", door->getKeyID())
	{
        Global<TaskQueue>::set(&taskQueue);
		room1.addObject(door);
		room1.addPlayer(player, "");
		session.clear();
	}
	~MudLockedDoorTest() {
		room1.removePlayer(player, "");
	}

	TaskQueue taskQueue;
	TestSession session;
	MudPlayer& player;
	TestRoom room1;
    DoorTestPortal* portal;
	MudLockedDoor* door;
    MudKey key;
};


TEST(MudLockedDoorTest, testDescription) {
    ASSERT_STR_EQUAL("descr. It's locked.", door->getDescription());

    door->unlock(player, key);
    ASSERT_STR_EQUAL("descr. It's closed.", door->getDescription());

    door->lock(player, key);
    ASSERT_STR_EQUAL("descr. It's locked.", door->getDescription());
}


TEST(MudLockedDoorTest, testLockUnlockingClosedDoor) {
    door->unlock(player, key);
	session.SESSION_CHECKWRITTEN("OK\n");
	room1.ROOM_CHECKWRITTEN("player unlocks the door.\n");

    door->unlock(player, key);
	session.SESSION_CHECKWRITTEN("It's not locked.\n");
	room1.ROOM_CHECKWRITTEN("");

    door->lock(player, key);
	session.SESSION_CHECKWRITTEN("OK\n");
	room1.ROOM_CHECKWRITTEN("player locks the door.\n");

    door->lock(player, key);
	session.SESSION_CHECKWRITTEN("It's already locked.\n");
	room1.ROOM_CHECKWRITTEN("");
}


TEST(MudLockedDoorTest, testLockUnlockingOpenDoor) {
    door->unlock(player, key);
	door->open(player);
    session.clear();
    
    door->lock(player, key);
	session.SESSION_CHECKWRITTEN("You should close it first.\n");
    door->unlock(player, key);
	session.SESSION_CHECKWRITTEN("It's not locked.\n");
}


TEST(MudLockedDoorTest, testOpeningClosingLockedDoor) {
    door->close(player);
	session.SESSION_CHECKWRITTEN("The door is already closed.\n");

	door->open(player);
	session.SESSION_CHECKWRITTEN("The door is locked.\n");
    
    door->unlock(player, key);
	session.SESSION_CHECKWRITTEN("OK\n");
    
	door->open(player);
	session.SESSION_CHECKWRITTEN("OK\n");

    door->close(player);
	session.SESSION_CHECKWRITTEN("OK\n");
}


TEST(MudLockedDoorTest, testPassingLockedDoor) {
	door->pass(player);
	session.SESSION_CHECKWRITTEN("The door is closed.\n");
    ASSERT_NULL(portal->passedPlayer);

    door->unlock(player, key);
    session.clear();
	door->pass(player);
	session.SESSION_CHECKWRITTEN("The door is closed.\n");
    ASSERT_NULL(portal->passedPlayer);

	door->open(player);
	door->pass(player);
    ASSERT_NOT_NULL(portal->passedPlayer);
}


TEST(MudLockedDoorTest, testResettingAfterOpening) {
    door->unlock(player, key);
    door->open(player);
    room1.clear();

    taskQueue.runNextTime();
    taskQueue.runNextTime();
    taskQueue.runNextTime();
	room1.ROOM_CHECKWRITTEN("");
    taskQueue.runNextTime();
	room1.ROOM_CHECKWRITTEN("The door closes.\n");
}


TEST(MudLockedDoorTest, testResettingAfterUnlocking) {
    door->unlock(player, key);
    room1.clear();

    taskQueue.runNextTime();
    taskQueue.runNextTime();
    taskQueue.runNextTime();
    ASSERT_STR_EQUAL("descr. It's closed.", door->getDescription());
    taskQueue.runNextTime();
    ASSERT_STR_EQUAL("descr. It's locked.", door->getDescription());
}


TEST(MudLockedDoorTest, testLockingWithWrongKey) {
    MudKey wrongKey(0, "", "", door->getKeyID() + 1);

    door->unlock(player, wrongKey);
	session.SESSION_CHECKWRITTEN("It's not the right key.\n");

    door->unlock(player, key);
    session.clear();

    door->lock(player, wrongKey);
	session.SESSION_CHECKWRITTEN("It's not the right key.\n");
}

